package com.jl15988.pguard;

import com.jl15988.pguard.enums.ProcessStatus;
import com.jl15988.pguard.exception.ProcessMonitorException;
import com.jl15988.pguard.function.ProcessErrorCallback;
import com.jl15988.pguard.function.ProcessOutputCallback;
import com.jl15988.pguard.model.ProcessConfig;
import com.jl15988.pguard.model.ProcessInfo;
import com.jl15988.pguard.platform.ProcessPlatform;
import com.jl15988.pguard.platform.ProcessPlatformConfig;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 进程池主类
 * <p>
 * 该类负责管理和监控多个进程，提供进程的生命周期管理、状态监控、资源回收等功能。
 * 支持进程的启动、停止、重启、暂停和恢复操作，并能够监控进程的CPU使用率、内存使用等信息。
 * 实现了AutoCloseable接口，支持自动资源管理。
 * </p>
 *
 * @author jl15988
 * @since 1.0.0
 */
@Slf4j
public class ProcessPool implements AutoCloseable {
    /**
     * 进程映射表，key为进程ID，value为进程实例
     */
    private final Map<String, ManagedProcess> processes = new ConcurrentHashMap<>();

    /**
     * 平台配置
     */
    private ProcessPlatformConfig platformConfig;

    /**
     * 全局输出回调函数，用于处理所有进程的标准输出
     */
    private ProcessOutputCallback globalOutputCallback;

    /**
     * 全局错误回调函数，用于处理所有进程的错误输出
     */
    private ProcessErrorCallback globalErrorCallback;

    /**
     * 进程监控执行器，用于定期收集进程信息
     */
    private final ScheduledExecutorService monitorExecutor;

    /**
     * 进程信息缓存，存储最新的进程监控信息
     */
    private final Map<String, ProcessInfo> processInfoCache = new ConcurrentHashMap<>();

    /**
     * 进程监控回调函数映射表，key为进程ID，value为监控回调函数
     */
    private final Map<String, Consumer<ProcessInfo>> processMonitorCallbacks = new ConcurrentHashMap<>();

    /**
     * 构造函数
     * <p>
     * 使用默认的平台配置初始化进程池。
     * </p>
     */
    public ProcessPool() {
        this(new ProcessPlatformConfig());
    }

    /**
     * 构造函数
     * <p>
     * 使用指定的平台配置初始化进程池。
     * </p>
     *
     * @param platformConfig 平台配置
     */
    public ProcessPool(ProcessPlatformConfig platformConfig) {
        this.monitorExecutor = Executors.newScheduledThreadPool(1, r -> {
            Thread t = new Thread(r, "ProcessPool-Monitor");
            t.setDaemon(true);
            return t;
        });

        this.platformConfig = platformConfig;
        startProcessMonitor();
    }

    /**
     * 添加进程到进程池
     * <p>
     * 将新的进程配置添加到进程池中进行管理。
     * 如果进程配置中设置了自动启动，则会在添加后立即启动进程。
     * 添加前会进行配置验证，确保必要的参数都已正确设置。
     * </p>
     *
     * @param config 进程配置，包含进程的所有必要信息
     * @return 进程实例
     * @throws ProcessMonitorException 如果添加过程中发生错误，如配置验证失败
     */
    public ManagedProcess addProcess(ProcessConfig config) {
        if (config.getProcessId() == null || config.getProcessId().trim().isEmpty()) {
            return null;
        }
        if (config.getCommand() == null || config.getCommand().trim().isEmpty()) {
            return null;
        }
        if (config.getPort() != null && (config.getPort() < 1 || config.getPort() > 65535)) {
            return null;
        }
        setGlobalProcessOutputCallback(config, globalOutputCallback);
        setGlobalProcessErrorCallback(config, globalErrorCallback);
        config.setPlatformConfig(platformConfig);
        config.setEnableMonitoring(false);
        ManagedProcess process = new ManagedProcess(config);
        processes.put(config.getProcessId(), process);

        return process;
    }

    /**
     * 验证进程配置
     * <p>
     * 检查进程配置的必要参数是否已正确设置，包括：
     * - 进程ID不能为空
     * - 命令不能为空
     * - 端口号必须在有效范围内（如果指定）
     * </p>
     *
     * @param config 进程配置
     * @throws ProcessMonitorException 如果配置验证失败
     */
    private void validateConfig(ProcessConfig config) {
        if (config.getProcessId() == null || config.getProcessId().trim().isEmpty()) {
            throw new ProcessMonitorException("进程ID不能为空");
        }
        if (config.getCommand() == null || config.getCommand().trim().isEmpty()) {
            throw new ProcessMonitorException("命令不能为空");
        }
        if (config.getPort() != null && (config.getPort() < 1 || config.getPort() > 65535)) {
            throw new ProcessMonitorException("端口号必须在1-65535之间");
        }
    }

    /**
     * 启动进程监控
     * <p>
     * 创建定时任务，定期收集所有进程的状态信息。
     * 监控信息包括CPU使用率、内存使用、运行时间等。
     * 默认每5秒更新一次进程信息。
     * </p>
     */
    private void startProcessMonitor() {
        monitorExecutor.scheduleAtFixedRate(() -> {
            try {
                updateProcessInfo();
            } catch (Exception e) {
                log.error("进程监控发生错误", e);
            }
        }, 0, 5, TimeUnit.SECONDS);
    }

    /**
     * 更新进程信息
     * <p>
     * 收集所有进程的最新状态信息，并更新进程信息缓存。
     * 如果进程设置了监控回调函数，则调用回调函数通知更新。
     * </p>
     */
    private void updateProcessInfo() {
        processes.forEach((processId, process) -> {
            try {
                process.updateStatus();
                ProcessInfo info = process.getProcessInfo();
                processInfoCache.put(processId, info);
                Consumer<ProcessInfo> callback = processMonitorCallbacks.get(processId);
                if (callback != null) {
                    callback.accept(info);
                }
            } catch (Exception e) {
                log.error("更新进程[{}]信息时发生错误", processId, e);
            }
        });
    }

    /**
     * 获取进程信息
     * <p>
     * 返回指定进程的最新监控信息，包括CPU使用率、内存使用等。
     * 如果进程不存在，则返回null。
     * </p>
     *
     * @param processId 进程ID
     * @return 进程信息，如果进程不存在则返回null
     */
    public ProcessInfo getProcessInfo(String processId) {
        return processInfoCache.get(processId);
    }

    /**
     * 获取所有进程信息
     * <p>
     * 返回进程池中所有进程的最新监控信息列表。
     * </p>
     *
     * @return 进程信息列表
     */
    public List<ProcessInfo> getAllProcessInfo() {
        return new ArrayList<>(processInfoCache.values());
    }

    /**
     * 设置进程监控回调
     * <p>
     * 为指定进程设置监控信息更新回调函数。
     * 当进程信息更新时，会调用此回调函数。
     * </p>
     *
     * @param processId 进程ID
     * @param callback  回调函数
     */
    public void setProcessMonitorCallback(String processId, Consumer<ProcessInfo> callback) {
        processMonitorCallbacks.put(processId, callback);
    }

    /**
     * 从进程池中移除进程
     * <p>
     * 停止并移除指定的进程，同时清理相关的监控信息和回调函数。
     * </p>
     *
     * @param processId 进程ID
     * @throws ProcessMonitorException 如果移除过程中发生错误
     */
    public void removeProcess(String processId) {
        try {
            ManagedProcess process = processes.get(processId);
            if (process != null) {
                process.stop();
                processes.remove(processId);
                processInfoCache.remove(processId);
                processMonitorCallbacks.remove(processId);
            }
        } catch (Exception e) {
            throw new ProcessMonitorException("移除进程失败: " + e.getMessage(), e);
        }
    }

    /**
     * 启动指定进程
     * <p>
     * 启动进程池中指定ID的进程。
     * 如果进程已经在运行，则会记录警告日志。
     * </p>
     *
     * @param processId 进程ID
     * @return 是否启动成功
     * @throws ProcessMonitorException 如果启动过程中发生错误
     */
    public boolean startProcess(String processId) {
        try {
            ManagedProcess process = processes.get(processId);
            if (process != null) {
                return process.start();
            }
            log.warn("进程[{}]不存在于进程池中", processId);
            return false;
        } catch (Exception e) {
            throw new ProcessMonitorException("启动进程失败: " + e.getMessage(), e);
        }
    }

    /**
     * 停止指定进程
     * <p>
     * 停止进程池中指定ID的进程。
     * 如果进程不存在，则会记录警告日志。
     * </p>
     *
     * @param processId 进程ID
     * @throws ProcessMonitorException 如果停止过程中发生错误
     */
    public void stopProcess(String processId) {
        try {
            ManagedProcess process = processes.get(processId);
            if (process != null) {
                process.stop();
            } else {
                log.warn("进程[{}]不存在于进程池中", processId);
            }
        } catch (Exception e) {
            throw new ProcessMonitorException("停止进程失败: " + e.getMessage(), e);
        }
    }

    /**
     * 重启指定进程
     * <p>
     * 重启进程池中指定ID的进程。
     * 会先停止进程，然后重新启动。
     * </p>
     *
     * @param processId 进程ID
     * @return 是否重启成功
     * @throws ProcessMonitorException 如果重启过程中发生错误
     */
    public boolean restartProcess(String processId) {
        try {
            ManagedProcess process = processes.get(processId);
            if (process != null) {
                return process.restart();
            }
            log.warn("进程[{}]不存在于进程池中", processId);
            return false;
        } catch (Exception e) {
            throw new ProcessMonitorException("重启进程失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取进程状态
     * <p>
     * 获取指定进程的当前状态。
     * 可能的状态包括：运行中、已停止、已暂停、错误等。
     * </p>
     *
     * @param processId 进程ID
     * @return 进程状态，如果进程不存在则返回null
     */
    public ProcessStatus getProcessStatus(String processId) {
        ManagedProcess process = processes.get(processId);
        if (process != null) {
            return process.getStatus().get();
        }
        return null;
    }

    /**
     * 判断进程是否在运行
     *
     * @param processId 进程ID
     * @return 进程状态是否为运行中
     */
    public boolean processIsRunning(String processId) {
        return getProcessStatus(processId) == ProcessStatus.RUNNING;
    }

    /**
     * 设置全局输出回调
     * <p>
     * 设置用于处理所有进程标准输出的回调函数。
     * 新添加的进程也会使用这个回调函数。
     * </p>
     *
     * @param callback 回调接口
     */
    public void setGlobalOutputCallback(ProcessOutputCallback callback) {
        this.globalOutputCallback = callback;
        processes.values().forEach(process -> {
            setGlobalProcessOutputCallback(process.getConfig(), globalOutputCallback);
        });
    }

    /**
     * 设置全局错误回调
     * <p>
     * 设置用于处理所有进程错误输出的回调函数。
     * 新添加的进程也会使用这个回调函数。
     * </p>
     *
     * @param callback 回调接口
     */
    public void setGlobalErrorCallback(ProcessErrorCallback callback) {
        this.globalErrorCallback = callback;
        processes.values().forEach(process -> {
            setGlobalProcessErrorCallback(process.getConfig(), globalErrorCallback);
        });
    }

    /**
     * 按状态过滤进程
     * <p>
     * 返回处于指定状态的所有进程ID列表。
     * </p>
     *
     * @param status 进程状态
     * @return 符合状态的进程ID列表
     */
    public List<String> filterProcessesByStatus(ProcessStatus status) {
        return processes.entrySet().stream()
                .filter(entry -> entry.getValue().getStatus().get() == status)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }

    /**
     * 批量操作进程
     * <p>
     * 对指定的多个进程执行相同的操作。
     * 如果某个进程不存在，则会跳过该进程。
     * </p>
     *
     * @param processIds 进程ID列表
     * @param operation  操作函数
     */
    public void batchProcessOperation(List<String> processIds, Consumer<ManagedProcess> operation) {
        processIds.stream()
                .map(processes::get)
                .filter(process -> process != null)
                .forEach(operation);
    }

    /**
     * 设置平台配置
     *
     * @param platformConfig 平台配置
     */
    public void setPlatformConfig(ProcessPlatformConfig platformConfig) {
        this.platformConfig = platformConfig;
    }

    /**
     * 获取平台配置
     *
     * @return 平台配置
     */
    public ProcessPlatformConfig getPlatformConfig() {
        return platformConfig;
    }

    /**
     * 获取当前平台
     *
     * @return 当前平台实现
     */
    public ProcessPlatform getCurrentPlatform() {
        return platformConfig.getCurrentPlatform();
    }

    /**
     * 关闭进程池
     * <p>
     * 停止所有进程，清理资源，关闭监控线程池。
     * 实现AutoCloseable接口，支持在try-with-resources语句中使用。
     * </p>
     *
     * @throws ProcessMonitorException 如果关闭过程中发生错误
     */
    @Override
    public void close() {
        try {
            // 停止所有进程
            processes.values().forEach(ManagedProcess::stop);
            processes.clear();
            processInfoCache.clear();
            processMonitorCallbacks.clear();

            // 关闭监控线程池
            monitorExecutor.shutdown();
            if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                monitorExecutor.shutdownNow();
            }
        } catch (Exception e) {
            log.error("关闭进程池时发生错误", e);
            throw new ProcessMonitorException("关闭进程池失败: " + e.getMessage(), e);
        }
    }

    /**
     * 设置全局输出回调
     *
     * @param config               进程配置
     * @param globalOutputCallback 全局输出回调
     */
    private void setGlobalProcessOutputCallback(ProcessConfig config, ProcessOutputCallback globalOutputCallback) {
        if (config.isEnableOutputCallback() || config.getOutputCallback() != null && globalOutputCallback != null) {
            config.setOutputCallback(globalOutputCallback);
        }
    }

    /**
     * 设置全局错误回调
     *
     * @param config              进程配置
     * @param globalErrorCallback 全局错误回调
     */
    private void setGlobalProcessErrorCallback(ProcessConfig config, ProcessErrorCallback globalErrorCallback) {
        if (config.isEnableErrorCallback() || config.getErrorCallback() != null && globalErrorCallback != null) {
            config.setErrorCallback(globalErrorCallback);
        }
    }
}
